using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;

namespace EnhancedTextBox
{
    public partial class FloatingForm : Form
    {
        public FloatingForm()
        {
            InitializeComponent();
        }

        /* This piece of code makes it possible to show the window
         * and not to set it active. */


        [DllImport("user32.dll")]
        private extern static IntPtr SetActiveWindow(IntPtr handle);


        private const int WM_ACTIVATE = 6;
        private const int WA_INACTIVE = 0;

        protected override void WndProc(ref Message m)
        {
            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            {
                if (m.Msg == WM_ACTIVATE)
                {
                    if (((int)m.WParam & 0xFFFF) != WA_INACTIVE)
                    {
                        if (m.LParam != IntPtr.Zero)
                        {
                            SetActiveWindow(m.LParam);
                        }
                        else
                        {
                            // Could not find sender, just in-activate it.
                            SetActiveWindow(IntPtr.Zero);
                        }
                    }
                }

                base.WndProc(ref m);    
            }

            base.WndProc(ref m);
        }

        private readonly List<string> items = new List<string>();

        /// <summary>
        /// Gets collection of items that are being proposed at the moment.
        /// </summary>
        public List<string> Items
        {
            get { return items; }
        }

        private readonly List<string> descriptions = new List<string>();

        /// <summary>
        /// Gets collection of descriptions of items that are being proposed at the moment.
        /// </summary>
        public List<string> Descriptions
        {
            get { return descriptions ; }
        }

        private void FloatingForm_Paint(object sender, PaintEventArgs e)
        {

            if (startViewingFrom > 0 && maximumVisibleWords >= items.Count)
            {
                startViewingFrom = 0;
            }

            Graphics g = e.Graphics;

            Rectangle bounds = new Rectangle(0, 0, Width-1, Height-1);
            g.FillRectangle(new LinearGradientBrush(bounds, topColor,bottomColor,90f),bounds);
            g.DrawRectangle(new Pen(Color.DarkGray),bounds);

            SolidBrush brush = new SolidBrush(Color.Black);
            SolidBrush brushGray = new SolidBrush(Color.DarkGray);
            Font fontI = new Font(font, FontStyle.Italic);

            int k = 0;
            for (int i = startViewingFrom; i < Math.Min(maximumVisibleWords+startViewingFrom, items.Count); i++ )
            {
                String str = items[i];
                String descr = null;
                if ( items.Count == descriptions.Count ) {
                   descr = descriptions[i];
                }

                Rectangle boundsThis = bounds;

                if (i == selectedIndex)
                {
                    boundsThis = new Rectangle(1, (font.Height +6)* k, Width - 2, font.Height + 6);
                    g.FillRectangle(new SolidBrush(selectionColor), boundsThis);
                }

                int descriptionLength = (int)g.MeasureString(descr, font).Width;

                g.DrawString(descr, fontI, brushGray, boundsThis.Width - descriptionLength - 5, 2 + k * (font.Height + 6));
                g.DrawString(str, font, brush, 5, 2 + k * (font.Height+6));
                
                k++;
            }

            if (maximumVisibleWords + startViewingFrom < items.Count)
            {
                Point [] arrow = new [] {
                    new Point(Width - 12,Height - 10),
                    new Point(Width -  4,Height - 10),
                    new Point(Width - 8, Height - 4)
                };

                g.FillPolygon(brush, arrow);
            }

            if (startViewingFrom > 0 )
            {
                Point[] arrow = new [] {
                    new Point(Width - 8, 4),
                    new Point(Width -  4, 10),
                    new Point(Width - 12, 10)
                };

                g.FillPolygon(brush, arrow);
            }

        }

        private int selectedIndex;

        public int SelectedIndex
        {
            get { return selectedIndex; }
            set 
            {
                if (value < 0)
                {
                    value = 0;
                }

                if ( items.Count > 0 && value > items.Count - 1)
                {
                    value = items.Count - 1;
                }

                if (value - startViewingFrom >= maximumVisibleWords)
                {
                    startViewingFrom++;
                }

                if (value - startViewingFrom < 0)
                {
                    startViewingFrom--;
                }

                selectedIndex = value; 
            }
        }

        private int startViewingFrom;

        private int maximumVisibleWords=4;

        /// <summary>
        /// Gets sets maximum number of visible proposed words.
        /// </summary>
        internal int MaximumVisibleWords
        {
            get { return maximumVisibleWords; }
            set { maximumVisibleWords = value; }
        }

        private Color selectionColor = Color.FromArgb(30, Color.Blue);

        /// <summary>
        /// Gets/sets selected item color.
        /// </summary>
        internal Color SelectionColor
        {
            get { return selectionColor; }
            set { selectionColor = value; }
        }

        private Color topColor = Color.White;

        /// <summary>
        /// Gets/sets top background color.
        /// </summary>
        internal Color TopColor
        {
            get { return topColor; }
            set { topColor = value; }
        }

        private Color bottomColor = Color.FromArgb(220, 220, 220);

        /// <summary>
        /// Gets/sets bottom background color.
        /// </summary>
        internal Color BottomColor
        {
            get { return bottomColor; }
            set { bottomColor = value; }
        }

        private Font font = new Font("Calibri", 8);

        public override Font Font {
            get { return font; }
            set { font = value; }
        }

    }
}